﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml.Serialization;
using Ionic.Zip;

namespace Packager
{
    public partial class frmPackager : Form
    {
        private string lastFilename = string.Empty;
        private TreeNode currentNode = null;
        private List<Solution> Solutions = new List<Solution>();

        public frmPackager()
        {
            InitializeComponent();
            LogManagement.LogToOutput += new EventHandler<LogToOutputEventArgs>(LogManagement_LogToOutput);
        }

        protected override void OnShown(EventArgs e)
        {
            LogManagement.LogMessage(string.Format("Project folder = {0}", Properties.Settings.Default.ProjectFolder));
            base.OnShown(e);
        }

        void LogManagement_LogToOutput(object sender, LogToOutputEventArgs e)
        {
            if (e.Output != null)
                BeginInvoke(new MethodInvoker(() => 
                { 
                    txtStatus.AppendText(e.Output + Environment.NewLine); 
                    Application.DoEvents(); 
                }));
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.ProjectFolder == string.Empty)
            {
                MessageBox.Show("Missing Project Folder");
                return;
            }
            fbdRoot.RootFolder = Environment.SpecialFolder.MyDocuments;
            fbdRoot.SelectedPath = Properties.Settings.Default.ProjectFolder;
            if (fbdRoot.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                txtRoot.Text = (Properties.Settings.Default.ProjectFolder.Length > 0 && fbdRoot.SelectedPath.Contains(Properties.Settings.Default.ProjectFolder))
                                    ? fbdRoot.SelectedPath.Substring(Properties.Settings.Default.ProjectFolder.Length+1)
                                    : fbdRoot.SelectedPath;

            btnLoad.Enabled = !string.IsNullOrEmpty(txtRoot.Text);
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            trvSource.Nodes.Clear();
            trvDest.Nodes.Clear();
            trvDest.Nodes.Add("Application folder");
            LoadFolders(txtRoot.Text, null);
        }

        private void LoadFolders(string folderRoot, TreeNode nodeRoot)
        {
            TreeNode curNode = nodeRoot == null
                        ? trvSource.Nodes.Add(Path.GetFileName(GetRootFolder(folderRoot)))
                        : nodeRoot.Nodes.Add(Path.GetFileName(GetRootFolder(folderRoot)));

            curNode.Tag = folderRoot;

            foreach (string dir in Directory.GetDirectories(GetRootFolder(folderRoot)))
                LoadFolders(GetProjectFolder(dir), curNode);

            foreach (string file in Directory.GetFiles(GetRootFolder(folderRoot)))
            {
                TreeNode fileNode = curNode.Nodes.Add(Path.GetFileName(file));
                fileNode.Tag = GetProjectFolder(file);
            }
        }


        private void trvSource_ItemDrag(object sender, ItemDragEventArgs e)
        {
            DoDragDrop(e.Item, DragDropEffects.Copy);
        }

        private void trvDest_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy;
        }

        private void trvDest_DragDrop(object sender, DragEventArgs e)
        {
            TreeNode NewNode;

            if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
            {
                Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
                TreeNode DestinationNode = ((TreeView)sender).GetNodeAt(pt);
                NewNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
                if (DestinationNode == null)
                {
                    trvDest.Nodes[0].Nodes.Add((TreeNode)NewNode.Clone());
                    trvDest.Nodes[0].Expand();
                }
                else if (DestinationNode.TreeView != NewNode.TreeView)
                {
                    DestinationNode.Nodes.Add((TreeNode)NewNode.Clone());
                    DestinationNode.Expand();
                    //Remove Original Node
                    //NewNode.Remove();
                }
            }
        }

        private void trvDest_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete && trvDest.SelectedNode != null &&
                trvDest.SelectedNode.Text != "Application folder")
                trvDest.SelectedNode.Remove();
        }
        private void tsmNew_Click(object sender, EventArgs e)
        {
            txtRoot.Text = string.Empty;
            trvSource.Nodes.Clear();
            trvDest.Nodes.Clear();
        }

        private void tsmLoad_Click(object sender, EventArgs e)
        {
            if (ofdLoad.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                LoadData(ofdLoad.FileName);
        }

        private void tsmSave_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(lastFilename))
                SaveData(lastFilename);
            else
                tsmSaveAs_Click(sender, e);
        }

        private void tsmSaveAs_Click(object sender, EventArgs e)
        {
            if (sfdSave.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                SaveData(sfdSave.FileName);
        }

        private void tsmPublish_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            string filename = string.Concat(txtApplication.Text, "_", txtVersion.Text);
            string zipfilename = string.Concat(filename, ".zip");
            string infoFilename = Path.ChangeExtension(txtApplication.Text, ".inf");
            FtpUploader.UploadFile(txtApplication.Text, zipfilename);
            FtpUploader.UploadFile(txtApplication.Text, infoFilename);
            this.Cursor = Cursors.Default;
        }

        private void tsmExit_Click(object sender, EventArgs e)
        {
            Application.ExitThread();
        }

        private void LoadData(string filename)
        {
            lastFilename = filename;
            XmlSerializer xSer = new XmlSerializer(typeof(SaveData));
            using (TextReader reader = new StreamReader(filename))
            {
                SaveData sData = (SaveData)xSer.Deserialize(reader);
                txtApplication.Text = sData.ApplicationName;
                txtVersion.Text = sData.Version;
                txtRoot.Text = sData.Structure.Path;
                trvSource.Nodes.Clear();
                trvDest.Nodes.Clear();
                Solutions = sData.SolutionsToBuild;
                LoadFolders(GetRootFolder(txtRoot.Text), null);
                CreateTreeFromStructure(trvDest.Nodes, sData.Structure);
            }
        }

        private void SaveData(string filename)
        {
            lastFilename = filename;
            SaveData sData = new SaveData();
            sData.ApplicationName = txtApplication.Text;
            sData.Version = txtVersion.Text;
            sData.Structure.Text = "Root";
            sData.Structure.Path = txtRoot.Text;
            sData.SolutionsToBuild = Solutions;
            CreateStructureFromTree(trvDest.Nodes, sData.Structure);
            XmlSerializer xSer = new XmlSerializer(typeof(SaveData));
            try
            {
                using (TextWriter writer = new StreamWriter(filename))
                {
                    xSer.Serialize(writer, sData);
                    writer.Close();
                }
            }
            catch (Exception e)
            {
                LogManagement.LogMessage(e.Message);
            }
        }

        private void CreateStructureFromTree(TreeNodeCollection nodes, SingleField sf)
        {
            foreach (TreeNode node in nodes)
            {
                SingleField cur = new SingleField();
                cur.Text = node.Text;
                cur.Path = node.Tag == null ? string.Empty : node.Tag.ToString();
                sf.Nodes.Add(cur);
                if (node.Nodes.Count > 0)
                    CreateStructureFromTree(node.Nodes, cur);
            }
        }

        private void CreateTreeFromStructure(TreeNodeCollection nodes, SingleField sf)
        {
            foreach (SingleField cur in sf.Nodes)
            {
                TreeNode node = new TreeNode(cur.Text);
                node.Tag = cur.Path;
                nodes.Add(node);
                if (cur.Nodes.Count > 0)
                    CreateTreeFromStructure(node.Nodes, cur);
            }
        }

        private void tsmCreate_Click(object sender, EventArgs e)
        {
            string filename = string.Concat(txtApplication.Text, "_", txtVersion.Text);
            string zipfilename = string.Concat(filename, ".zip");
            string infoFilename = Path.ChangeExtension(txtApplication.Text, ".inf");
            if (File.Exists(zipfilename))
                File.Delete(zipfilename);
            using (ZipFile zipfile = new ZipFile(zipfilename))
            {
                CreateZipFromTree(trvDest.Nodes, zipfile);
                zipfile.Save();
            }

            InfoData infoData = new InfoData();
            infoData.Version = txtVersion.Text;
            infoData.Filename = zipfilename;

            XmlSerializer xSer = new XmlSerializer(typeof(InfoData));
            try
            {
                using (TextWriter writer = new StreamWriter(infoFilename))
                {
                    xSer.Serialize(writer, infoData);
                    writer.Close();
                }
            }
            catch (Exception ex)
            {
                LogManagement.LogMessage(ex.Message);
                return;
            }

            LogManagement.LogMessage("Created Zip");
        }

        private void CreateZipFromTree(TreeNodeCollection nodes, ZipFile zip)
        {
            foreach (TreeNode node in nodes)
            {
                if (node.Tag!= null && File.Exists(GetRootFolder(node.Tag.ToString())))
                {
                    string root = Path.Combine(Properties.Settings.Default.ProjectFolder, txtRoot.Text);
                    //string readRoot = node.Tag.ToString().Substring(txtRoot.Text.Length);
                    string folder = Path.GetDirectoryName(node.Tag.ToString().Substring(txtRoot.Text.Length));
                    string file = GetRootFolder(node.Tag.ToString());
                    zip.AddFile(file, folder);
                }
                if (node.Nodes.Count > 0)
                    CreateZipFromTree(node.Nodes, zip);
            }
        }

        private void tsmiGetVersion_Click(object sender, EventArgs e)
        {
            string path = Path.Combine(Path.Combine(Properties.Settings.Default.ProjectFolder, txtRoot.Text, currentNode.Text));
            txtVersion.Text = AssemblyName.GetAssemblyName(path).Version.ToString(3);
        }

        private void trvSource_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                Point pt = new Point(e.X, e.Y);
                trvSource.PointToClient(pt);
                currentNode = trvSource.GetNodeAt(pt);
            }
        }

        private void tsmSolutions_Click(object sender, EventArgs e)
        {
            frmSolutions frm = new frmSolutions();
            frm.Solutions = Solutions;
            if (frm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                Solutions = frm.Solutions;
        }

        private void tsmSetting_Click(object sender, EventArgs e)
        {
            MessageBox.Show(Properties.Settings.Default.ProjectFolder);
            fbdRoot.SelectedPath = Properties.Settings.Default.ProjectFolder;
            if (fbdRoot.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                Properties.Settings.Default.ProjectFolder = fbdRoot.SelectedPath;
                Properties.Settings.Default.Save();
            }
        }

        private string GetRootFolder(string folder)
        {
            if (Properties.Settings.Default.ProjectFolder.Length == 0)
                MessageBox.Show("Missing Project Folder in Setting", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);

            return Properties.Settings.Default.ProjectFolder.Length > 0
                ? Path.Combine(Properties.Settings.Default.ProjectFolder, folder)
                : folder;
        }


        private string GetProjectFolder(string dir)
        {
            if (Properties.Settings.Default.ProjectFolder.Length == 0)
                MessageBox.Show("Missing Project Folder in Setting", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);

            if (dir.StartsWith(Properties.Settings.Default.ProjectFolder))
                return dir.Substring(Properties.Settings.Default.ProjectFolder.Length + 1);
            else
                return dir;
        }

        private void tsbBuild_Click(object sender, EventArgs e)
        {
            foreach (Solution sol in Solutions)
            {
                Builder.Build(sol.SolutionFile, sol.SolutionConfig);
            }
        }

        void Builder_MessageLog(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (!txtStatus.InvokeRequired)
                txtStatus.Text += e.Data + Environment.NewLine;
            else
                txtStatus.BeginInvoke(new MethodInvoker(() => { txtStatus.AppendText(e.Data + Environment.NewLine); }));

            Application.DoEvents();
        }

        private void tsbClear_Click(object sender, EventArgs e)
        {
            txtStatus.Text = string.Empty;
        }
    }
}
